package tiltmaze.generator;

import tiltmaze.Cell;
import tiltmaze.Maze;
import tiltmaze.RowCol;

/**
 * Class containing 3 static methods for neighbourhood moves
 * @author Juraj Niznan
 */
public class MazeGenMoves {

    /**
     * NUMBER OF POSSIBLE VERTICAL WALLS: (c-1)*r
     * NUMBER OF POSSIBLE HORIZONTAL WALLS: (r-1)*c
     * adds a wall if the maze blueprint does not contain a wall at specified
     * place or removes it if it does
     * @param index from 0 to (2*r*c - r - c)
     */
    public static Maze switchWall(Maze m, int index) {
        int r = m.getNumOfRows();
        int c = m.getNumOfCols();
        int[][] cellCodes = m.getCellCodes();
        if (index >= 0 && index < (c - 1) * r) {
            //vertical wall
            int i = index / (c - 1);
            int j = index % (c - 1);
            Cell leftCell = new Cell(cellCodes[i][j]);
            if (leftCell.getWalls()[1]) {
                //remove wall
                cellCodes[i][j] -= 2;
                cellCodes[i][j + 1] -= 8;
            } else {
                //add wall
                cellCodes[i][j] += 2;
                cellCodes[i][j + 1] += 8;
            }
        }
        index -= (c - 1) * r;
        if (index >= 0 && index < (r - 1) * c) {
            //horizontal wall
            int i = index / (c);
            int j = index % (c);
            Cell topCell = new Cell(cellCodes[i][j]);
            if (topCell.getWalls()[2]) {
                //remove wall
                cellCodes[i][j] -= 4;
                cellCodes[i + 1][j] -= 1;
            } else {
                //add wall
                cellCodes[i][j] += 4;
                cellCodes[i + 1][j] += 1;
            }
        }
        return new Maze(cellCodes);
    }

    /**
     * moves the start one cell in the specified direction
     * @param m
     * @param direction 0=up, 1=right, 2=down, 3=left
     */
    public static Maze moveStart(Maze m, int direction) {
        RowCol rc = m.getStart();
        switch (direction) {
            case 0:
                rc.row = (rc.row == 0) ? 0 : rc.row - 1;
                break;
            case 1:
                rc.col = (rc.col == m.getNumOfCols() - 1) ? rc.col : rc.col + 1;
                break;
            case 2:
                rc.row = (rc.row == m.getNumOfRows() - 1) ? rc.row : rc.row + 1;
                break;
            case 3:
                rc.col = (rc.col == 0) ? 0 : rc.col - 1;
                break;
        }
        Cell[][] cells = m.getCells();
        if (!cells[rc.row][rc.col].getCheckpoint()) {
            cells[m.getStart().row][m.getStart().col].setStart(false);
            cells[rc.row][rc.col].setStart(true);
            return new Maze(cells);
        } else {
            return m;
        }
    }

    /**
     * Moves a specified checkpoint one cell in the specified direction
     * @param m
     * @param source
     * @param direction 0=up, 1=right, 2=down, 3=left
     * @return
     */
    public static Maze moveCheckpoint(Maze m, RowCol source, int direction) {
        Cell[][] cells = m.getCells();
        if (cells[source.row][source.col].getCheckpoint()) {
            RowCol rc = new RowCol(source);
            switch (direction) {
                case 0:
                    rc.row = (rc.row == 0) ? 0 : rc.row - 1;
                    break;
                case 1:
                    rc.col = (rc.col == m.getNumOfCols() - 1) ? rc.col : rc.col + 1;
                    break;
                case 2:
                    rc.row = (rc.row == m.getNumOfRows() - 1) ? rc.row : rc.row + 1;
                    break;
                case 3:
                    rc.col = (rc.col == 0) ? 0 : rc.col - 1;
                    break;
            }
            if (cells[rc.row][rc.col].getCheckpoint() || cells[rc.row][rc.col].getStart()) {
                return m;
            }
            cells[source.row][source.col].setCheckpoint(false);
            cells[rc.row][rc.col].setCheckpoint(true);
            return new Maze(cells);
        }
        return m;
    }
}
